<?php
/**
 * Created by PhpStorm.
 * User: happy
 * Date: 18/5/26
 * Time: 下午5:17
 * 参考:https://blog.csdn.net/ty_hf/article/details/50959993
 */
//自己写的redis集成操作函数
class redis2{
    public static $link_arr ='';//连接数组
    private static $redis_obj=null ;//redis实例化时静态变量


    public static function connect($config=array()) {
        if(!$config){//如果config文件不存在就取配置文件的连接
            if(self::$link_arr){$config=self::$link_arr;}
            else{
                die('from redis2.php line17,no redis config set in $config[redis]  or no param take in to the function connect');
            }
        }
         $redis=new Redis();
         $flag=$redis->connect($config['host'],$config['port'],$config['time_out']);
        if(!$flag){
          $msg=  $redis->getLastError();
            exit('redis error:'.$msg);
        }
        $flag= $redis->auth($config['pass']);
        if(!$flag){
            $msg=  $redis->getLastError();
            exit('redis auth error:'.$msg);
        }
         self::$redis_obj = $redis;
    }
    //持久连接
    public static  function  pconnect($config=array()){
        if(!$config){//如果config文件不存在就取配置文件的连接
            if(self::$link_arr){$config=self::$link_arr;}
            else{
                die('from redis2.php line40,no redis config set in $config[redis]  or no param take in to the function connect');
            }
        }
        $redis=new Redis();
        $flag=$redis->pconnect($config['host'],$config['port'],$config['time_out']);
        if(!$flag){
            $msg=  $redis->getLastError();
            exit('redis error:'.$msg);
        }
        $flag= $redis->auth($config['pass']);
        if(!$flag){
            $msg=  $redis->getLastError();
            exit('redis auth error:'.$msg);
        }
        self::$redis_obj = $redis;
    }

    public  static  function  get_redis(){
        if(!is_object(self::$redis_obj)){self::connect();}
        return self::$redis_obj;
    }

    //事务机制
    public static function begin(){
        return self::$redis_obj->multi();
    }
    public static  function commit(){
        return self::$redis_obj->exec();
    }

    public static  function  ping(){
        return self::$redis_obj->ping();
    }
    public  static  function  key_number(){
        return self::$redis_obj->dbSize();
    }

    //键值类型
    public  static  function type($key){
        return self::$redis_obj->type($key);
    }
    //字符串追加
    public  static   function  append($key,$value=''){
        return self::$redis_obj->append($key,$value);
    }
    public  static  function  substr($key ,$start, $end){
        return self::$redis_obj->substr($key ,$start, $end);
    }
    public  static  function strlen($key){
        return self::$redis_obj->strlen($key);
    }

    /*设置字符串,存活时间是秒钟数,过期会自动删除*/
    public static function set($key, $value, $timeout=0) {
        if(!is_object(self::$redis_obj)){self::connect();}
        if ($timeout > 0){ $setRes =self::$redis_obj->setex($key, $timeout,$value);}
        else{
            $setRes =  self::$redis_obj->set($key, $value);
        }
        return $setRes;
    }

    /**
     * 查，获取 某键对应的值，不存在返回false
     * @param $key ,键值
     * @return bool|string ，查询成功返回信息，失败返回false
     */
    public static function get($key){
        if(!is_object(self::$redis_obj)){self::connect();}
        $setRes =  self::$redis_obj->get($key);//不存在返回false
        if($setRes === 'false'){
            return false;
        }
        return $setRes;
    }
    //设置键值的过期时间
    public  static  function  expire($key, $ttl){
      return self::$redis_obj->expire($key,$ttl);
    }



    /**
     * 增，构建一个列表(先进后出，类似栈)在左侧追加元素,持续追加
     * @param String $key KEY名称
     * @param string $value 值
     * @param $timeout |num  过期时间
     */
    public static function push_stack($key,$value,$timeout=0){
        if(!is_object(self::$redis_obj)){self::connect();}
//          echo "$key - $value \n";
        if(is_array($value)){
            foreach($value as $val){
                $re = self::$redis_obj->LPUSH($key,$val);
            }
        }
        else{
            $re = self::$redis_obj->LPUSH($key,$value);
        }
        if ($timeout > 0) self::$redis_obj->expire($key, $timeout);
        return $re;
    }

    /**
     * 增，构建一个列表(先进先出，类似队列)
     * @param string $key KEY名称
     * @param string $value 值
     * @param $timeout |num  过期时间
     */
    public static function push_list($key,$value,$timeout=0){
        if(!is_object(self::$redis_obj)){self::connect();}
        $re=true;
        if(is_array($value)){
            foreach($value as $val){
                $re = self::$redis_obj->RPUSH($key,$val);
            }
        }
        else{
            $re = self::$redis_obj->RPUSH($key,$value);//
        }

        if ($timeout > 0)  self::$redis_obj->expire($key, $timeout);
        return $re;
    }

    //获取队列元素
    public static  function  get_list($key,$head=0,$tail=-1){
        return self::$redis_obj->lrange($key,$head,$tail);
    }

    //添加集合,集合中元素肯定不会重复,如果已经添加了返回false
    public static  function  add_collection($key,$value,$timeout = 0){
        $re=true;
        if(is_array($value)){
            foreach($value as $val){
                $re = self::$redis_obj->sadd($key,$val);
            }
        }
        else{$re = self::$redis_obj->sadd($key,$value);  }

        if ($timeout > 0) self::$redis_obj->expire($key, $timeout);
        return $re;
    }


    /**
     * 集合的接口必须用集合的函数才能获取
     * 查，取集合对应元素
     * @param string $key 集合名字
     */
    public static function get_collection($key){
        $re =   self::$redis_obj->exists($key);//存在返回1，不存在返回0
        if(!$re) return false;
        return self::$redis_obj->sMembers($key);
    }
   //获取两个集合内相同的元素,取交集
    public  static  function  get_collection_same($key1,$key2=''){
        return self::$redis_obj->sInter($key1,$key2);
    }
    //获取集合并集
    public  static  function  get_collection_combine($key1,$key2=''){
        return self::$redis_obj->sunion($key1,$key2);
    }
    //获取集合差集
    public  static  function  get_collection_diff($key1,$key2=''){
        return self::$redis_obj->sdiff($key1,$key2);
    }
    //发布订阅消息不保存,没有rabbitmq实用
    public  static  function  publish($channel_key,$msg=''){
        return self::$redis_obj->publish($channel_key,$msg);
    }
  //$redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
    /* //订阅消息
    public  function  subscribe(){
       redis2::subscribe(array('msg_queen'),function($redis, $chan, $msg){

            write('receive msg:'.$chan.'==>'.$msg);
       });
    }
     * function f($redis, $chan, $msg) {
     *  switch($chan) {
     *      case 'chan-1':
     *          ...
     *          break;
     *
     *      case 'chan-2':
     *                     ...
     *          break;
     *
     *      case 'chan-2':
     *          ...
     *          break;
     *      }
     * }*/
    public  static  function  subscribe($channel=array(),$callback){
      return  self::$redis_obj->subscribe($channel, $callback);
    }

    /*
        * 增，改，构建一个集合(有序集合),支持批量写入,更新
        * @param string $key 集合名称
        * @param array $score_value key为scoll, value为该权的值
        * @return int 插入操作成功返回插入数量【,更新操作返回0】
        */
    public static function set_order_collection($key,$score_value,$timeout =0){
        if(!is_array($score_value)) return false;
        $a = 0;//存放插入的数量
        foreach($score_value as $score=>$value){
            $re =  self::$redis_obj->zadd($key,$score,$value);//当修改时，可以修改，但不返回更新数量
            $re && $a+=1;
        }
        if ($timeout > 0)  self::$redis_obj->expire($key, $timeout);
        return $a;
    }

    /**
     * 查，有序集合查询，可升序降序,默认从第一条开始，查询一条数据
     * @param $key ,查询的键值
     * @param $min ,从第$min条开始
     * @param $max，查询的条数
     * @param $order ，asc表示升序排序，desc表示降序排序
     * @return array|bool 如果成功，返回查询信息，如果失败返回false
     */
    public static function get_order_collection($key,$start = 0 ,$end =-1,$order = 'desc'){
        $re =    self::$redis_obj->exists($key);//存在返回1，不存在返回0
        if(!$re) return false;//不存在键值
        if('desc' == strtolower($order)){
            $re = self::$redis_obj->zrevrange($key,$start ,$end);
        }else{
            $re =  self::$redis_obj->zrange($key,$start ,$end);
        }
        if(!$re) return false;//查询的范围值为空
        return $re;
    }

    /**
     * 返回集合key中，成员member的排名
     * @param $key，键值
     * @param $member，scroll值
     * @param $type ,是顺序查找还是逆序
     * @return bool,键值不存在返回false，存在返回其排名下标
     */
    public static function get_order_collection_rank($key,$member,$type = 'desc'){
        $type = strtolower(trim($type));
        if($type == 'desc'){
            $re =  self::$redis_obj->zrevrank($key,$member);//其中有序集成员按score值递减(从大到小)顺序排列，返回其排位
        }else{
            $re =  self::$redis_obj->zrank($key,$member);//其中有序集成员按score值递增(从小到大)顺序排列，返回其排位
        }
        if(!is_numeric($re)) return false;//不存在键值
        return $re;
    }

    /**
     * 返回名称为key的zset中score >= star且score <= end的所有元素
     * @param $key
     * @param $member
     * @param $star，
     * @param $end,
     * @return array
     */
    public static function get_order_collection_by_score($key,$score_min,$score_max){
        return  self::$redis_obj->ZRANGEBYSCORE($key,$score_min,$score_max);
    }

    /**
     * 返回名称为key的zset中元素member的score 获取权重
     * @param $key
     * @param $member
     * @return string ,返回查询的member值
     */
    public  static function  get_member_score($key,$member){
        return  self::$redis_obj->ZSCORE($key,$member);
    }

    /*------------------------------------5.hash结构----------------------------------------------------*/
    /**
     * 增，以json格式插入数据到缓存,hash类型
     * @param $redis_key |array , $redis_key['key']数据库的表名称;$redis_key['field'],下标key
     * @param $token,该活动的token，用于区分标识
     * @param $id,该活动的ID，用于区分标识
     * @param $data|array ，要插入的数据,
     * @param $timeout ，过期时间，默认为0
     * @return $number 插入成功返回1【,更新操作返回0】
     */
    public static function set_hash($key,$hash_key,$data,$timeout = 0){
        if(is_array($data)){
            $data = json_encode($data);
        }
        $re =self::$redis_obj -> hSet($key,$hash_key,$data);//存入缓存
        if ($timeout > 0) self::$redis_obj->expire($key, $timeout);//设置过期时间
        return $re;
    }

    /**
     * 查，json形式存储的哈希缓存，有值则返回;无值则查询数据库并存入缓存
     * @param $redis,$redis['key'],$redis['field']分别是hash的表名称和键值
     * @param $token,$token为公众号
     * @param $token,$id为活动ID
     * @return bool|array, 成功返回要查询的信息，失败或不存在返回false
     */
    public static function get_hash($key,$hash_key){
        $re =  self::$redis_obj->hexists($key,$hash_key);//返回缓存中该hash类型的field是否存在
        if($re){
            $info = self::$redis_obj->hget($key,$hash_key);
            if(substr($info,0,1)=='{'){
                $info = json_decode($info,true);
            }

        }else{
            $info = false;
        }
        return $info;
    }

    /**
     * 增，普通逻辑的插入hash数据类型的值
     * @param $key ,键名
     * @param $data |array 一维数组，要存储的数据
     * @param $timeout |num  过期时间
     * @return $number 返回OK【更新和插入操作都返回ok】
     */
    public static function set_multi_hash($key,$data=array(),$timeout=0){
        $re = self::$redis_obj -> hmset($key,$data);
        if ($timeout > 0) self::$redis_obj->expire($key, $timeout);
        return $re;
    }

    public  static  function  get_multi_hash($key,$hash_keys=array()){
     return self::$redis_obj->hMGet($key,$hash_keys);
    }

    /**
     * 查，普通的获取值
     * @param $key,表示该hash的下标值
     * @return array 。成功返回查询的数组信息，不存在信息返回false
     */
    public static function hash_val($key){
        $re =  self::$redis_obj->exists($key);//存在返回1，不存在返回0
        if(!$re) return false;
        $vals =self::$redis_obj -> hvals($key);
        $keys =self::$redis_obj -> hkeys($key);
        $re = array_combine($keys,$vals);
        foreach($re as $k=>$v){
            if(!is_null(json_decode($v))){
                $re[$k] = json_decode($v,true);//true表示把json返回成数组
            }
        }
        return $re;
    }
    /*------------------------------------其他结构----------------------------------------------------*/
    /**
     * 设置自增,自减功能
     * @param $key ，要改变的键值
     * @param int $num ，改变的幅度，默认为1
     * @param string $member ，类型是zset或hash，需要在输入member或filed字段
     * @param string $type，类型，default为普通增减,还有:zset,hash
     * @return bool|int 成功返回自增后的scroll整数，失败返回false
     */
    public static function increase($key,$num = 1,$member = '',$type=''){
        $num = intval($num);
        switch(strtolower(trim($type))){
            case "zset":
                $re = self::$redis_obj->zIncrBy($key,$num,$member);//增长权值
                break;
            case "hash":
                $re =  self::$redis_obj->hincrby($key,$member,$num);//增长hashmap里的值
                break;
            default:
                if($num > 0){
                    $re =  self::$redis_obj->incrby($key,$num);//默认增长
                }else{
                    $re = self::$redis_obj->decrBy($key,-$num);//默认增长
                }
                break;
        }
        if($re) return $re;
        return false;
    }


    /**
     * 清除缓存
     * @param int $type 默认为0，清除当前数据库；1表示清除所有缓存
     */
   public  static function  flush($type = 0){
        if($type) {
            self::$redis_obj->flushAll();//清除所有数据库
        }else{
            self::$redis_obj->flushdb();//清除当前数据库
        }
       return true;
    }

    /**
     * 检验某个键值是否存在
     * @param $keys ，键值
     * @param string $type，类型，默认为常规
     * @param string $field。若为hash类型，输入$field
     * @return bool
     */
    public static function exists($keys,$type = '',$field=''){
        switch(strtolower(trim($type))){
            case 'hash':
                $re =  self::$redis_obj->hexists($keys,$field);//有返回1，无返回0
                break;
            default:
                $re =  self::$redis_obj->exists($keys);
                break;
        }
        return $re;
    }

    /**
     * 删除缓存
     * @param string|array $key，键值
     * @param $type，类型，默认为常规，还有hash,zset
     * @param string $field,hash=>表示$field值，set=>表示value,zset=>表示value值，list类型特殊暂时不加
     * @return int | ，返回删除的个数
     */
    public static function delete($key,$type='',$field = ''){
        switch(strtolower(trim($type))){
            case 'hash':
                $re =  self::$redis_obj->hDel($key,$field);//返回删除个数
                break;
            case 'set':
                $re =  self::$redis_obj->sRem($key,$field);//返回删除个数
                break;
            case 'zset':
                $re =  self::$redis_obj->zDelete($key,$field);//返回删除个数
                break;
            default:
                $re =  self::$redis_obj->del($key);//返回删除个数
                break;
        }
        return $re;
    }

    //日志记录
    public static function logger($log_content,$position = 'user')
    {
        $max_size = 1000000;   //声明日志的最大尺寸1000K

        $log_dir = './log';//日志存放根目录

        if(!file_exists($log_dir)) mkdir($log_dir,0777);//如果不存在该文件夹，创建

        if($position == 'user'){
            $log_filename = "{$log_dir}/User_redis_log.txt";  //日志名称
        }else{
            $log_filename = "{$log_dir}/Wap_redis_log.txt";  //日志名称
        }

        //如果文件存在并且大于了规定的最大尺寸就删除了
        if(file_exists($log_filename) && (abs(filesize($log_filename)) > $max_size)){
            unlink($log_filename);
        }

        //写入日志，内容前加上时间， 后面加上换行， 以追加的方式写入
        file_put_contents($log_filename, date('Y-m-d_H:i:s')." ".$log_content."\n", FILE_APPEND);
    }


   public static function close()
    {
        self::$redis_obj->close();
    }






}
